﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using XNADota.Camera;
using XNADota.ContentPipeline.Data;
using XNADota.Input;
using XNADota.MDX;
using XNADota.Primitive;
using XNADota.Terrain;
using XNADota.Graphics;
using XNADota.Graphics.Techniques;
using XNADota.UI;
using XNADota.GamePlay;
using JigLibX.Physics;
using JigLibX.Collision;
using JiggleGame.PhysicObjects;
using Microsoft.Xna.Framework.Input;

namespace XNADota
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class XNADota : Microsoft.Xna.Framework.Game, IInputHandler
    {
        private GraphicsDeviceManager _GraphicsDeviceManager;
        private GameSettings _Settings;

        private CameraManager _CameraManager;
        private InputManager _InputManager;
        private LandscapeRenderer _LandscapeRenderer;
        private FrameRate _FrameRate;
        private ScreenCapturer _ScreenCapturer;
        private PostScreen _PostScreen;
        private PhysicsSystem _PhysicSystem;
        private CarObject _CarObject;

        public CameraManager CameraManager
        {
            get { return _CameraManager; }
        }

        public InputManager InputManager
        {
            get { return _InputManager; }
        }

        private static XNADota _Instance = null;
        public static XNADota Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new XNADota();
                return _Instance;
            }
        }

        private XNADota()
        {
            Content.RootDirectory = "Content";

            _Settings = Content.Load<GameSettings>("Data\\Settings");

            InitializeGraphics();
        }

        protected void InitializeGraphics()
        {
            _GraphicsDeviceManager = new GraphicsDeviceManager(this);
            IsMouseVisible = _Settings.IsMouseVisible;

            _GraphicsDeviceManager.PreferredBackBufferWidth = _Settings.ScreenWidth;
            _GraphicsDeviceManager.PreferredBackBufferHeight = _Settings.ScreenHeight;
            _GraphicsDeviceManager.IsFullScreen = _Settings.IsFullScreen;
            _GraphicsDeviceManager.ApplyChanges();

            _GraphicsDeviceManager.GraphicsDevice.DepthStencilBuffer =
                                new DepthStencilBuffer(_GraphicsDeviceManager.GraphicsDevice,
                                                       _GraphicsDeviceManager.GraphicsDevice.DisplayMode.Width,
                                                       _GraphicsDeviceManager.GraphicsDevice.DisplayMode.Height,
                                                       DepthFormat.Depth24Stencil8);
            _GraphicsDeviceManager.ApplyChanges();
        }

        protected override void Initialize()
        {
            // Create Game Core
            Landscape landscape = Content.Load<Landscape>(_Settings.TerrainConfigPath);
            GameplayWorld.Instance.CreateLogicMembers(this, landscape);

            SceneManager.Instance.Initialize(this, _Settings);

            // Create Game Components
            ReplaceableTextures.Initialize(Content);
            _CameraManager = new CameraManager(this);
            _InputManager = new InputManager(this);
            _FrameRate = new FrameRate(this);
            _ScreenCapturer = new ScreenCapturer(this);
            _FrameRate.DisplayFrameRate = _Settings.DisplayFrameRate;
            DebugPrimitiveRenderer.Initialize(_GraphicsDeviceManager.GraphicsDevice);
            _PostScreen = new PostScreen(this);

            GameplayWorld.Instance.Initialize(this);

            // Graphics device should be initialize here, but why? (put it in the ctor is incorrect.)
            RenderStateManager.Instance.Initialize(_GraphicsDeviceManager.GraphicsDevice);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            InitializePhysicsSystem();

            // load landscape
            Landscape landscape = Content.Load<Landscape>(_Settings.TerrainConfigPath);
            _LandscapeRenderer = new LandscapeRenderer(this, _GraphicsDeviceManager.GraphicsDevice, landscape);

            var camera = new OverLookCamera(this, landscape);
            float margin = 6 * 128;
            float minX = 0, minZ = 0, maxX = 128 * 128, maxZ = 128 * 128;
            camera.SetBoundary(minX + margin, minZ + margin, maxX - margin, maxZ - margin);
            _CameraManager.Add(camera);

            _InputManager.Add(camera);
            _InputManager.Add(this);
            _InputManager.Add(_ScreenCapturer);

            GameplayWorld.Instance.PrepareAll();

            BuildPhysicsObjects();
        }

        private void BuildPhysicsObjects()
        {
            // map surface
            new HeightmapObject(this, null, new Vector2(8192.0f, 8192.0f));

            //Microsoft.Xna.Framework.Graphics.Model sphereModel = this.Content.Load<Microsoft.Xna.Framework.Graphics.Model>("Models\\Physics\\sphere");
            //_SphereObject = new SphereObject(this, sphereModel, 10.0f, Matrix.Identity, new Vector3(8192.0f, 1400.0f, 8192.0f));

            //Microsoft.Xna.Framework.Graphics.Model carModel = this.Content.Load<Microsoft.Xna.Framework.Graphics.Model>("Models/Physics/car");
            //Microsoft.Xna.Framework.Graphics.Model wheelModel = this.Content.Load<Microsoft.Xna.Framework.Graphics.Model>("Models/Physics/wheel");
            //_CarObject = new CarObject(this, carModel, wheelModel, true, true, 60.0f, 5.0f, 14.7f, 15.0f, 1.20f, 0.4f * 100, 0.05f * 100, 0.90f, 0.001f, 1, 52000.0f, _PhysicSystem.Gravity.Length());
            //_CarObject.Car.Chassis.Body.MoveTo(new Vector3(8192.0f, 1000.0f, 8192.0f), Matrix.Identity);
            //_CarObject.Car.EnableCar();
            //_CarObject.Car.Chassis.Body.AllowFreezing = false;
            //this.Components.Add(_CarObject);

            //var _BoxObject = new BoxObject(this, carModel, new Vector3(30, 30, 30), Matrix.Identity, new Vector3(8192.0f, 1400.0f, 8192.0f));
            //this.Components.Add(_BoxObject);
        }
        private void InitializePhysicsSystem()
        {
            _PhysicSystem = new PhysicsSystem();

            //physicSystem.CollisionSystem = new CollisionSystemGrid(32, 32, 32, 30, 30, 30);
            //physicSystem.CollisionSystem = new CollisionSystemBrute();
            _PhysicSystem.CollisionSystem = new CollisionSystemSAP();

            _PhysicSystem.EnableFreezing = true;
            _PhysicSystem.SolverType = PhysicsSystem.Solver.Normal;
            _PhysicSystem.CollisionSystem.UseSweepTests = true;

            _PhysicSystem.NumCollisionIterations = 8;
            _PhysicSystem.NumContactIterations = 8;
            _PhysicSystem.NumPenetrationRelaxtionTimesteps = 15;
        }

        protected override void UnloadContent()
        {
            Content.Unload();
        }

        // AI
        // Behavior
        // Physics/Kinetic
        // Graphics
        protected override void Update(GameTime gameTime)
        {
            // update frame rate
            _FrameRate.Update(gameTime);

            // input as the AI layer
            _InputManager.Update(gameTime);

            GameplayWorld.Instance.Update(gameTime);

            _PhysicSystem.Integrate((float)gameTime.ElapsedGameTime.TotalSeconds);

            GameplayWorld.Instance.UpdateAfterIntegration();

            // update cameras before graphics layer
            _CameraManager.Update(gameTime);

            SceneManager.Instance.Update(gameTime, _CameraManager.ActiveCamera);

            //System.Windows.Forms.Cursor.Clip = new System.Drawing.Rectangle(
            //    Window.ClientBounds.X, Window.ClientBounds.Y,
            //    Window.ClientBounds.Width, Window.ClientBounds.Height);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            _GraphicsDeviceManager.GraphicsDevice.Clear(Color.TransparentBlack);

            //position = new Vector3(32 * 111, 0, (float)gameTime.TotalGameTime.TotalSeconds * 128);
            //position = GameplayWorld.Instance.PathManager.GetHeight(position);
            //Matrix world = Matrix.CreateTranslation(position + new Vector3(0, 0, 0));
            //_MdxRenderer.Draw(Matrix.CreateRotationX(-(float)Math.PI / 2.0f) * world, CameraManager.ActiveCamera.View, CameraManager.ActiveCamera.Projection);

            //_LandscapeRenderer.Draw(Matrix.Identity, CameraManager.ActiveCamera.View, CameraManager.ActiveCamera.Projection);
            //_MdxRenderer.Draw(Matrix.Identity, CameraManager.ActiveCamera.View, CameraManager.ActiveCamera.Projection);

            GameplayWorld.Instance.Draw(gameTime);

            SceneManager.Instance.Render();

            DebugPrimitiveRenderer.Draw(CameraManager.ActiveCamera.View, CameraManager.ActiveCamera.Projection);
            _PostScreen.Render();

            base.Draw(gameTime);
        }

        public void ProcessInput(GameTime gameTime, InputState inputState)
        {
            if (inputState.IsKeyJustDown(Microsoft.Xna.Framework.Input.Keys.Escape))
                this.Exit();

            //if (inputState.IsKeyDown(Keys.W) || inputState.IsKeyDown(Keys.S))
            //{
            //    if (inputState.IsKeyDown(Keys.W))
            //        _CarObject.Car.Accelerate = 1.0f;
            //    else
            //        _CarObject.Car.Accelerate = -1.0f;
            //}
            //else
            //    _CarObject.Car.Accelerate = 0.0f;

            //if (inputState.IsKeyDown(Keys.A) || inputState.IsKeyDown(Keys.D))
            //{
            //    if (inputState.IsKeyDown(Keys.A))
            //        _CarObject.Car.Steer = 1.0f;
            //    else
            //        _CarObject.Car.Steer = -1.0f;
            //}
            //else
            //    _CarObject.Car.Steer = 0.0f;

            //if (inputState.IsKeyDown(Keys.B))
            //    _CarObject.Car.HBrake = 1.0f;
            //else
            //    _CarObject.Car.HBrake = 0.0f;
        }
    }
}
